//run mpi solver
//$Id: run_mpi.h 678 2011-02-27 18:14:42Z Oleg.Bulychov $
#pragma once

#include "src/heo/include/ini_file.h"
#include "src/heo/include/fast_stream.h"
#include "src/heo/include/transmit_string.h"
#include "src/heo/include/transmit_vector.h"
#include "src/heo/include/easy_mpi.h"
#include "src/heo/include/run_environment.h"


struct check_point_mpi//:for weak_hybrid<>
{
    template<class Solver, class Pool>
    static bool check(Solver& solver, Pool& pool)
    {
        double end_time = mpi::implementation::time();
        std::cout << "half time = " << end_time - solver.get_Environment()->start_time_ << " s" << lf;
        //pool.sort();
        pool.output(solver.get_Environment());
        return true;
    }
};

template<class TSolver>
struct Run_MPI
{
    typedef typename TSolver::TEnvironment      TEnvironment;
    typedef typename TEnvironment::TProblem     TProblem;
    typedef typename TEnvironment::TSolution    TSolution;
    typedef typename TEnvironment::TConfig      TConfig;
    typedef typename TEnvironment::TStatistics  TStatistics;

    int retval_;
    operator int() const { return retval_; }

    int run(int argc, char* argv[], int& rank)
    {
        using namespace std;

        setlocale(LC_ALL, "C");//:decimal point must be '.'

        mpi::implementation impl(argc, argv);
        mpi::communicator comm;
        rank  = comm.rank();
        int np = comm.size();
        cout << "rank " << rank << " from " << np << lf;

        TEnvironment env;
        TSolver solver;

        if (0 == rank)
        {
            if (argc != 3)
            {
                cout << "usage: " << argv[0] << " randseed file.xml" << endl;
                comm.abort();
                return -1;
            }
            for (int i = 0; i < argc; ++i)
            {
                cout << "argv[" << i << "]='" << argv[i] << "'" << endl;
            }
            env.rand_seed_ = ::strtol(argv[1], NULL, 0);
            bool load_ok = env.property_tree_.load(argv[2]);
            mpi::bcast(0, comm) << env.property_tree_ << env.rand_seed_;
            
            //:desctructive parser!
            env.property_tree_.parse();
            xml::in in(env.property_tree_);
            in >> env;
            in >> solver;

            meta::ostream_visitor kcout(cout);
            kcout << env;
            cout << endl;
            kcout << solver;
            cout << endl;

            if (!load_ok)
            {
                xml::out out(env.property_tree_);
                out << env;
                out << solver;
                env.property_tree_.save(argv[2]);
                cout << "new file '" << argv[2] << "' created" << endl;
                comm.abort();
                return -1;
            }
        }
        else
        {
            mpi::bcast(0, comm) >> env.property_tree_ >> env.rand_seed_;
            env.property_tree_.parse();
            xml::in in(env.property_tree_);
            in >> env;
            in >> solver;
        }

        random_generator::randomize(1, env.rand_seed_, rank);

        if (env.input_file_.length() > 0)
        {
            fast_istream2 ifs;
            if (!ifs.open(env.input_file_.c_str()))
            {
                cout << "ERROR: can not open '" << env.input_file_ << "'" << endl;
                comm.abort();
                return -1;
            }
            random_generator rnd(0);
            if (!env.problem_.read(ifs, &env, rnd))
            {
                cout << "ERROR: '" << env.input_file_ << "' corrupted" << endl;
                comm.abort();
                return -1;
            }
        }
        else
        {
            if (env.problem_.get_Default_File_Name() && *env.problem_.get_Default_File_Name())
            {
                cout << "ERROR: input file missing" << endl;
                comm.abort();
                return -1;
            }
        }

        comm.barrier();

        double start_time = impl.time();
        env.start_time_ = start_time;
        solver.init(&env, rank, 0);
        Solution_Pool<TSolution> pool;
        TStatistics stat;
        if (!pool.init(solver, 1) || !stat.init(&env, 1))//:1 - max threads per process
        {
            comm.abort();
            return -1;
        }
        for (int n = 1; n <= env.run_count_; ++n)
        {
            solver.solve(pool, stat, comm);
        }
        double end_time = impl.time();

        if (0 == rank)
        {
            cout << "time = " << end_time - start_time << " s" << lf;
            pool.sort();
            pool.output(&env);
        }
        stat.output(&env, solver, "mpi", np, rank);
        return 0;
    }

    Run_MPI(int argc, char* argv[])
    {
        int rank;
        retval_ = run(argc, argv, rank);
#if defined(_MSC_VER)
        if (0 == rank)
        {
            extern void If_Debug_Press_Enter();
            If_Debug_Press_Enter();
        }
#endif
    }
};

//EOF!
